<template>
  <scroll-view class="kg-scroll-view-virtual-list" enable-back-to-top="true" scroll-y
      refresher-enabled="true" :refresher-triggered="showRefreshLoading"
      @scroll="scrollEvent"
      @refresherrefresh="refresherrefresh1" @scrolltolower="scrolltolower">

    <div class="infinite-list-phantom" :style="phantomStyle"></div>
    <div class="infinite-list" :style="contentStyle">
      <slot :list="visibleData"></slot>
    </div>

    <kg-page-footer :show-loading="isShowFooterLoading" :show-no-more="isShowFooterNoMore"></kg-page-footer>
    <kg-empty-box class="kg-full" v-if="!initLoading && list.length === 0"></kg-empty-box>
  </scroll-view>
</template>

<script>
export default {
  props: {
    pageSize: {
      type: Number,
      default: 15,
    },
    // 超过n条数据展示没有更多的提示
    showNoMoreMinLength: {
      type: Number,
      default: 10,
    },
    query: {
      type: Object,
      deault: () => {
        return {}
      },
    },
    getDataFunc: {
      type: Function,
    },

    // 预估高度
    estimatedItemSize: {
      type: Number,
      default: 100,
    },
    // 缓冲区比例
    bufferScale: {
      type: Number,
      default: 1,
    },
  },
  data () {
    return {
      showRefreshLoading: false,
      initLoading: true, // 初始化加载的标识
      loading: false,
      currentPage: 0,
      total: 0,
      list: [],
      isShowFooterLoading: false,
      isShowFooterNoMore: false,

      phantomHeight: 0,
      startOffset: 0,
      // 可视区域高度
      screenHeight: 0,
      // 起始索引
      start: 0,
      // 结束索引
      end: 0,
    }
  },
  computed: {
    pages () {
      return Math.ceil(this.total / this.pageSize)
    },

    phantomStyle () {
      return `height: ${this.phantomHeight}px;`
    },
    contentStyle () {
      return `transform: translate3d(0, ${this.startOffset}px, 0)`
    },
    _listData () {
      return this.list.map((item, index) => {
        return {
          _index: `_${index}`,
          ...item,
        }
      })
    },
    visibleCount () {
      return Math.ceil(this.screenHeight / this.estimatedItemSize)
    },
    aboveCount () {
      return Math.min(this.start, this.bufferScale * this.visibleCount)
    },
    belowCount () {
      return Math.min(this.list.length - this.end, this.bufferScale * this.visibleCount)
    },
    visibleData () {
      const start = this.start - this.aboveCount
      const end = this.end + this.belowCount
      return this._listData.slice(start, end)
    },
  },
  watch: {
    query () {
      this.refresh()
    },
    visibleData () {
      console.log('visibleData change', this.start, this.end, this.visibleCount, this.visibleData)
      this.setVirtualList()
    },
  },
  mounted () {
    this.getData()
    this.initVirtualList()
  },
  updated () {

  },
  methods: {
    // 初始化虚拟列表
    initVirtualList () {
      // TODO:
      // this.screenHeight = this.$el.clientHeight
      this.screenHeight = 667
      this.start = 0
      this.end = this.start + this.visibleCount
    },
    //  初始化位置信息
    initPositions () {
      this.positions = this.list.map((d, index) => ({
        index,
        height: this.estimatedItemSize,
        top: index * this.estimatedItemSize,
        bottom: (index + 1) * this.estimatedItemSize,
      }))
    },

    // 在组建中不能命名 refresherrefresh、scrolltolower事件名不能命名为 onPullDownRefresh、 onReachBottom，会报错
    //  下拉被触发
    refresherrefresh1 (e) {
      this.refresh()
    },
    //  滚动到底部被触发
    scrolltolower () {
      this.getData()
    },

    refresh () {
      this.currentPage = 0
      this.getData(true)
    },
    // 第一次onLoad时请求用getData()方法，refresh()会将showRefreshLoading设置为true,顶部出现加载样式
    getData (isRefresh = false) {
      if (this.loading || (this.currentPage > 0 && this.currentPage >= this.pages)) {
        return
      }
      // 只有下拉刷新时，设置showRefreshLoading, showRefreshLoading会控制顶部加载框是否显示
      if (isRefresh) {
        this.showRefreshLoading = true
      }
      const query = Object.assign({
        offset: this.currentPage * this.pageSize,
        limit: this.pageSize,
      }, this.query)

      this.loading = true
      this.setFooter()
      uni.showNavigationBarLoading()
      const isShowRequestLoading = !this.initLoading && isRefresh
      this.getDataFunc(query, isShowRequestLoading).then(({ items, count }) => {
        this.currentPage++
        this.total = count
        const list = items
        if (!isRefresh) {
          this.list.push(...list)
        } else {
          this.list = list
        }
        this.initPositions()
      }).finally(() => {
        if (this.initLoading) {
          this.initLoading = false
        }
        this.showRefreshLoading = false
        this.loading = false
        this.setFooter()
        uni.hideNavigationBarLoading()
      })
    },
    setFooter () {
      let isShowFooterLoading = false
      let isShowFooterNoMore = false
      if (this.initLoading || (this.loading && this.currentPage > 0 && this.currentPage !== this.pages)) {
        isShowFooterLoading = true
      }
      if (!this.loading && this.list.length > this.showNoMoreMinLength && this.currentPage === this.pages) {
        isShowFooterNoMore = true
      }
      this.isShowFooterLoading = isShowFooterLoading
      this.isShowFooterNoMore = isShowFooterNoMore
    },

    //  虚拟列表发生变化后更新位置信息
    setVirtualList () {
      this.$nextTick(function () {
        // TODO:
        // if (!this.$refs.items || !this.$refs.items.length) {
        //   return
        // }

        // 获取真实元素大小，修改对应的尺寸缓存
        // this.updateItemsSize()
        // // 更新列表总高度
        // const height = this.positions[this.positions.length - 1].bottom
        // this.phantomHeight = height
        // // 更新真实偏移量
        // this.setStartOffset()
      })
    },

    // 获取列表起始索引
    getStartIndex (scrollTop = 0) {
      // 二分法查找
      return this.binarySearch(this.positions, scrollTop)
    },
    binarySearch (list, value) {
      let start = 0
      let end = list.length - 1
      let tempIndex = null

      while (start <= end) {
        const midIndex = parseInt((start + end) / 2)
        const midValue = list[midIndex].bottom
        if (midValue === value) {
          return midIndex + 1
        } else if (midValue < value) {
          start = midIndex + 1
        } else if (midValue > value) {
          if (tempIndex === null || tempIndex > midIndex) {
            tempIndex = midIndex
          }
          end = end - 1
        }
      }
      return tempIndex
    },
    // 获取列表项的当前尺寸
    updateItemsSize () {
      console.log('ss', this.visibleData)
      const query = uni.createSelectorQuery().in(this)
      query.selectAll('.list-item').boundingClientRect((res) => {
        console.log('item', res)
        // this.groupListHeight = res.map(x => {
        //   return { top: x.top, height: x.height }
        // })
      }).exec()

      // const nodes = this.$refs.items
      // nodes.forEach((node) => {
      //   const rect = node.getBoundingClientRect()
      //   const height = rect.height
      //   const index = +node.id.slice(1)
      //   const oldHeight = this.positions[index].height
      //   const dValue = oldHeight - height
      //   // 存在差值
      //   if (dValue) {
      //     this.positions[index].bottom = this.positions[index].bottom - dValue
      //     this.positions[index].height = height
      //     for (let k = index + 1; k < this.positions.length; k++) {
      //       this.positions[k].top = this.positions[k - 1].bottom
      //       this.positions[k].bottom = this.positions[k].bottom - dValue
      //     }
      //   }
      // })
    },
    // 获取当前的偏移量
    setStartOffset () {
      let startOffset
      if (this.start >= 1) {
        const size = this.positions[this.start].top - (this.positions[this.start - this.aboveCount] ? this.positions[this.start - this.aboveCount].top : 0)
        startOffset = this.positions[this.start - 1].bottom - size
      } else {
        startOffset = 0
      }
      this.startOffset = startOffset
    },
    // 滚动事件
    scrollEvent (e) {
      // 当前滚动位置
      const scrollTop = e.detail.scrollTop
      // 此时的开始索引
      this.start = this.getStartIndex(scrollTop)
      // 此时的结束索引
      this.end = this.start + this.visibleCount
      // 此时的偏移量
      this.setStartOffset()
    },
  },
}
</script>

<style lang="scss">
.kg-scroll-view-virtual-list{
  flex: 1;
  width:100%;
  overflow: hidden;
  position: relation;

  .infinite-list-phantom {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    z-index: -1;
  }

  .infinite-list {
    left: 0;
    right: 0;
    top: 0;
    position: absolute;
  }
}
</style>
